// --------------------------------------------------------------------------------------------------------------------
// <copyright file="CompositionDependencyResolver.cs" company="ecblog.codeplex.com">
//   emrecoskun.net - ibrahimatay.com
// </copyright>
// <summary>
//   Composition dependency resolver
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace EcBlog.Composition
{
    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.ComponentModel.Composition.Hosting;
    using System.ComponentModel.Composition.Primitives;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Web.Mvc;

    using EcBlog.Data.Sql;

    using Elmah;

    /// <summary>
    /// Composition dependency resolver
    /// </summary>
    [Export(typeof(IDependencyResolver))]
    [Export(typeof(CompositionDependencyResolver))]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class CompositionDependencyResolver
        : IDependencyResolver
    {
        private static IDependencyResolver defaultDependencyResolver;
        private static CompositionContainer container;
        private static IDependencyResolver staticFactory;

        /// <summary>
        /// Initializes a new instance of the <see cref="CompositionDependencyResolver"/> class.
        /// </summary>
        [ImportingConstructor]
        public CompositionDependencyResolver()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CompositionDependencyResolver"/> class.
        /// </summary>
        /// <param name="catalog">The catalog.</param>
        public CompositionDependencyResolver(ComposablePartCatalog catalog)
            : this(catalog, DependencyResolver.Current)
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CompositionDependencyResolver"/> class.
        /// </summary>
        /// <param name="catalog">The catalog.</param>
        /// <param name="defaultDependencyResolver">The default dependency resolver.</param>
        public CompositionDependencyResolver(ComposablePartCatalog catalog, IDependencyResolver defaultDependencyResolver)
        {
            if (CompositionDependencyResolver.defaultDependencyResolver == null)
            {
                CompositionDependencyResolver.defaultDependencyResolver = defaultDependencyResolver;
            }

            if (container == null)
            {
                container = new CompositionContainer(catalog);
            }

            if (ReferenceEquals(staticFactory, null))
            {
                staticFactory = this;
            }
        }

        /// <summary>
        /// Gets the static factory.
        /// </summary>
        public static IDependencyResolver StaticFactory
        {
            get
            {
                BuildContainer();
                return staticFactory;
            }
        }

        /// <summary>
        /// Gets the container.
        /// </summary>
        public CompositionContainer Container
        {
            get
            {
                return container;
            }
        }

        /// <summary>
        /// Resolves singly registered services that support arbitrary object creation.
        /// </summary>
        /// <param name="serviceType">The type of the requested service or object.</param>
        /// <returns>
        /// The requested service or object.
        /// </returns>
        public object GetService(Type serviceType)
        {
            try
            {
                var exports = container.GetExports(serviceType, null, null).ToList();
                if (exports.Any())
                {
                    return exports.First().Value;
                }

                var result = defaultDependencyResolver.GetService(serviceType);
                if (result != null)
                {
                    container.ComposeParts(result);
                }

                return result;
            }
            catch (ReflectionTypeLoadException exception)
            {
                ErrorSignal.FromCurrentContext().Raise(exception);
                return null;
            }
            catch (Exception)
            {
                return null;
            }
        }

        /// <summary>
        /// Resolves multiply registered services.
        /// </summary>
        /// <param name="serviceType">The type of the requested services.</param>
        /// <returns>
        /// The requested services.
        /// </returns>
        public IEnumerable<object> GetServices(Type serviceType)
        {
            try
            {
                var exports = container.GetExports(serviceType, null, null).ToList();
                if (exports.Any())
                {
                    return exports.Select(e => e.Value).AsEnumerable();
                }

                var result = defaultDependencyResolver.GetServices(serviceType);
                container.ComposeParts(result);
                return result;
            }
            catch (ReflectionTypeLoadException exception)
            {
                var b = new StringBuilder();
                if (exception.LoaderExceptions != null)
                {
                    exception.LoaderExceptions.ToList().ForEach(c => b.AppendLine(c.Message));
                }

                return null;
            }
            catch (Exception)
            {
                return null;
            }
        }

        private static void BuildContainer()
        {
            if (!ReferenceEquals(staticFactory, null))
            {
                return;
            }

            var catalog = new AggregateCatalog(new AssemblyCatalog(typeof(EcModelContext).Assembly));
            var dependencyResolver = new CompositionDependencyResolver(catalog, DependencyResolver.Current);
            DependencyResolver.SetResolver(dependencyResolver);
        }
    }
}